<?php
/**
 * File Class
 *
 * @package Extensions
 * @version @@VERSION@@
 * @subpackage Low-Cal_Vanilla
 * @copyright Damien Lebrun 2007
 * @author Damien Lebrun
 * @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GPLv2
 */


/**
 * File Class, to get details and content of a file
 * @package Extensions
 * @subpackage Low-Cal_Vanilla
 */
class LowCalVanilla_File {
	/**
	 * @var string
	 */
	var $Extension;
	/**
	 * @var int Unix timestamp of the last modication of the file
	 */
	var $LastModification;
	/**
	 * @var string
	 */
	var $Name;
	/**
	 * @var string Absolute path
	 */
	var $Path;
	/**
	 * @var Context
	 */
	var $Context;
	/**
	 * @access private
	 * @var handle
	 */
	var $_fp;

	/**
	 * Constructor
	 * @return void
	 * @param string|array $Path
	 * @param string $Mime Mime type
	 * @param string $Encoding Content encoding
	 */
	function LowCalVanilla_File(&$Context, $Path) {
		$this->Context = &$Context;

		$this->SetPath($Path);
	}

	function AppendContent($Data, $CloseAfter = 1) {

		if (is_string($Data)
			&& $this->Writable() !== -1
			&& $this->_SetFileHandler('ab', $CloseAfter)
		) {
			$return = $this->_Append($Data);
			if ($CloseAfter) {
				$this->Close();
			}
			return $return;
		}

		return false;
	}

	/**
	 * return the content of the file - use include, so it will parse php.
	 * @return string
	 * @param int $Length Lenght of text to get from the file
	 * @param bool $CloseAfter Should the new file handler be closed after reading
	 * @param string $Mode Specifies the type of access you require to the stream {@link http://uk.php.net/manual/en/function.fopen.php#id2760008}
	 */
	function GetContent($Length = null, $CloseAfter = true, $Mode = 'rb') {
		if ($this->Exist()) {
			if (!$this->_SetFileHandler($Mode)) {
				return false;
			}
			$return = $this->_Read($Length);
			if ($CloseAfter) {
				$this->Close();
			}
			return $return;
		}
	}

	/**
	 * Get file extension
	 * @return string
	 */
	function GetExtension()
	{
		if($this->Extension) {
			return $this->Extension;
		}

		if ($this->Exist()) {
			$Name = $this->GetName();
			if (strstr($Name, '.')) {
				$this->Extension = substr(strrchr($Name, '.'), 1, strlen($Name));
			} else {
				$this->Extension = "";
			}
			return $this->Extension;
		}
		return false;
	}

	/**
	 * Get Last modification of the file
	 * @return int
	 */
	function GetLastModification() {
		if ($this->LastModification) {
			return $this->LastModification;
		}

		if ($this->Exist()) {
			$this->LastModification = filemtime($this->GetPath());
			return $this->LastModification;
		}

		return false;
	}

	/**
	 * Get base name of the file
	 * @return string
	 */
	function GetName() {
		if ($this->Exist()
		&& !$this->Name
		) {
			$this->Name = basename($this->GetPath());
			return $this->Name;
		}
		return $this->Name;
	}

	/**
	 * Get the path to the file
	 * @return string
	 */
	function GetPath() {
		return $this->Path;
	}

	/**
	 * Get size of the file
	 * @return int
	 */
	function GetSize() {
		if ($this->Exist()) {
			return filesize($this->GetPath());
		}
	}

	/**
	 * Check if the file has changed
	 * @return bool
	 * @param int $Time Time to compare - Unix timestamps.
	 */
	function HasChanged($Time) {
		if ($this->Exist()) {
			return ($Time < $this->GetLastModification()) ;
		}
	}

	/**
	 * Check if a file exist
	 * @return bool
	 */
	function Exist() {
		if ($this->Path) {
			return (file_exists($this->Path)
					&& is_file($this->Path));
		}
		return false;
	}

	/**
	 * Set the path
	 * @return string
	 * @param string|array $Path
	 */
	function SetPath($Path) {
		if (is_array($Path)) {
			$Path = str_replace('\\', '/', $Path);
			$Path = implode('/', $Path);
		}
		return $this->Path = $Path;
	}

	/**
	 * Unlick the file
	 * @return bool
	 */
	function Unlink() {
		if($this->Exist()) {
			$this->Close();
			return unlink($this->GetPath());
		}
	}

	/**
	 * Write data to file
	 * @return int number of bytes written
	 * @param string $Data Text to writet to the file
	 * @param bool $CloseAfter Should it close the file after operation
	 * (new mode and file pointer back to the start of the file)
	 */
	function SetContent($Data, $CloseAfter = 1) {

		if (!is_string($Data)) {
			return false;
		}

		if ($this->Writable() === -1) {
			return false;
		}

		if (!$this->_SetFileHandler('wb')) {
			return false;
		}

		$return = $this->_Append($Data);
		if ($CloseAfter) {
			$this->Close();
		}
		return $return;
	}

	/**
	 * Reset properties - will close
	 * @return void
	 */
	function Clear() {
		$this->Close();
		$this->Extension = '';
		$this->LastModification = 0;
		$this->Name = '';
		$this->Path = '';
		$this->_fp = null;
	}

	/**
	 * Open file (with fopen)
	 * @return handler
	 * @param string $Mode {@link http://uk.php.net/manual/en/function.fopen.php#id2760008}
	 */
	function Open($Mode) {
		$this->Close();
		return $this->_SetFileHandler($Mode);
	}

	/**
	 * Close file (fclose)
	 * @return bool
	 */
	function Close() {
		$this->_UnlockFileHandler();
		return $this->_Fclose();
	}

	/**
	 * Check a file is writable
	 * @return int 1 -> writable, 0 -> can be created, -1 -> can't be created either
	 */
	function Writable() {
		if ($this->Exist()) {
			if (is_writable($this->GetPath())) {
				return 1;
			}
		} else {
			$dir = dirname($this->GetPath());
			if (file_exists($dir)
			&& is_writable($dir)
			) {
				return 0;
			} else {
				return -1;
			}
		}
	}

	/**
	 * Close file handler
	 * @access private
	 * @return bool
	 * @access private
	 */
	function _Fclose() {
		if (isset($this->_fp)) {
			$return = @fclose($this->_fp);
			unset($this->_fp);
			return $return;
		}
	}

	/**
	 * Read content
	 * @access private
	 * @return string
	 * @param int $Length
	 */
	function _Read($Length = null) {
		if (!$Length) {
			$Length = $this->GetSize();
		}
		$Mqr = get_magic_quotes_runtime();
		set_magic_quotes_runtime(0);
		$Content = @fread($this->_fp, $Length);
		set_magic_quotes_runtime($Mqr);
		return $Content;
	}

	/**
	 * Append content to the file
	 * @access private
	 * @return int Number of bytes written
	 * @param string $Data
	 */
	function _Append($Data) {
		$return = false;
		if ($this->_fp) {
			$Mqr = get_magic_quotes_runtime();
			set_magic_quotes_runtime(0);
			$return = fwrite($this->_fp, $Data);
			set_magic_quotes_runtime($Mqr);
		}
		return $return;
	}

	/**
	 * Set the file handler
	 * @access private
	 * @return handler
	 * @param string mode
	 * @param bool $Overwrite Replace current file handler
	 */
	function _SetFileHandler($Mode) {
		$locked = true;

		if(!isset($this->_fp)) {
			$this->_fp = @fopen($this->Path, $Mode);
			$locked = false;
			if (strpos('r', $Mode)) {
				$locked = $this->_LockFileHandler(LOCK_SH);
			} else {
				$locked = $this->_LockFileHandler(LOCK_EX);
			}
		}

		if (!$locked) {
			$this->Close();
			return false;
		}

		return $this->_fp;
	}

	/**
	 * Lock a file
	 * @access private
	 * @return bool
	 * @param operation $Operation {@link http://uk.php.net/manual/en/function.flock.php}
	 */
	function _LockFileHandler($Operation = LOCK_EX) {
		$tries = 5;
		$locked = false;
		while ($tries > 0 ) {
			$locked = flock($this->_fp, $Operation | LOCK_NB);
			if ($locked) {
				$tries = 0;
			} else {
				sleep(1);
				$tries--;
			}
		}
		return $locked;
	}

	/**
	 * Unlock a file
	 * @access private
	 * @return bool
	 */
	function  _UnlockFileHandler() {
		if (isset($this->_fp)) {
			fflush($this->_fp);
			return @flock($this->_fp, LOCK_UN);
		}
		return true;
	}

}